﻿#region Includes

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;
using Object = ArtheaEngine.Model.Object;
using NLog;

#endregion

namespace ArtheaEngine.Model
{
    public abstract partial class Character : IEntity, IWritable, IFormattable, IDisposable, ICloneable
    {
        private static readonly Logger Log = LogManager.GetCurrentClassLogger();

        private static readonly string[] HeShe = {"it", "he", "she", "it"};
        private static readonly string[] HimHer = {"it", "him", "her", "it"};
        private static readonly string[] HisHer = {"its", "his", "her", "it"};

        public Character()
        {
        }

        object ICloneable.Clone()
        {
            return this.Clone();
        }

        public Character Clone()
        {
            return MemberwiseClone() as Character;
        }

        public void Equip(Object obj, WearLocation location)
        {
            if (!obj.WearFlags.And(Flag.MakeBit((int)location)))
            {
                Log.Warn("Cannot equip object {0} on {1}", obj, location);
                return;
            }

            Carrying.Add(new CharObj{Object=obj, Character=this, WearLocation =location});
        }

        public virtual void Act(ActType type, string format, params object[] args)
        {
            var temp = new List<object> {this};
            temp.AddRange(args);
            args = temp.ToArray();

            if ((type & ActType.Char) != 0)
            {
                WriteLine(string.Format(new ActionFormatter(this), format, args));
            }

            if ((type & ActType.Room) != 0)
            {
                foreach (var ch in Room.Characters)
                {
                    if (ch == this) continue;

                    ch.WriteLine(string.Format(new ActionFormatter(ch), format, args));
                }
            }

            if ((type & ActType.NotChar) != 0)
            {
                foreach (var ch in Room.Characters)
                {
                    if (ch == this) continue;

                    if (args.Length > 1 && args[1] is Character && args[1] == ch)
                        continue;

                    ch.WriteLine(string.Format(new ActionFormatter(ch), format, args));
                }
            }

            if ((type & ActType.World) != 0)
            {
                foreach (var ch in World.Connections)
                {
                    ch.WriteLine(string.Format(new ActionFormatter(ch.Playing), format, args));
                }
            }
        }

        public virtual bool CanSee(Character who)
        {
            return true;
        }

        public virtual bool Equals(Character obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return obj.Id == Id;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (Character)) return false;
            return Equals((Character) obj);
        }

        public override int GetHashCode()
        {
            return Id.GetHashCode();
        }

        public override string ToString()
        {
            return Name;
        }

        public virtual string ToString(string format)
        {
            return ToString(format, CultureInfo.CurrentCulture);
        }

        public abstract void Save();

        public abstract World World { get; }

        #region IDisposable Members

        /// <summary>
        ///                     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public virtual void Dispose()
        {
            if (Room != null)
            {
                Room.Characters.Remove(this);
                Room = null;
            }
        }

        #endregion

        #region IFormattable Members

        public virtual string ToString(string format, IFormatProvider formatProvider)
        {
            if (string.IsNullOrEmpty(format) || format == "G")
            {
                if (formatProvider is ActionFormatter)
                {
                    var viewer = ((ActionFormatter) formatProvider).Viewer;

                    if (!viewer.CanSee(this))
                        return "someone";
                }

                return Name.ToString(formatProvider);
            }

            var prop = GetType().GetProperty(format, BindingFlags.Public | BindingFlags.Instance
                                                     | BindingFlags.IgnoreCase);

            if (prop != null)
            {
                return prop.GetValue(this, null).ToString();
            }

            switch (char.ToUpper(format[0]))
            {
                case 'M':
                    return HimHer[Sex.Value];
                case 'S':
                    return HisHer[Sex.Value];
                case 'E':
                    return HeShe[Sex.Value];
                default:
                    throw new FormatException("unable to locate property");
            }
        }

        #endregion

        #region IWritable Members

        public abstract void WriteLine();
        public abstract void WriteLine(object obj);
        public abstract void WriteLine(string format, params object[] args);
        public abstract void Write(string format, params object[] args);
        public abstract void Write(byte[] value);
        public abstract void Write(object obj);
        public abstract void Page(StringBuilder buf);

        #endregion

    }

    public partial class CharObj
    {
    }

    [Flags]
    public enum ActType
    {
        Char = (1 << 0),
        Room = (1 << 1),
        NotChar = (1 << 2),
        World = (1 << 3)
    }

    public partial class PositionWrapper
    {
        public Position EnumValue
        {
            get { return (Position)Value; }
            set { Value = (int)value; }
        }

        public static implicit operator Position(PositionWrapper wrapper)
        {

            if (wrapper == null) return default(Position);

            return wrapper.EnumValue;
        }
        public static implicit operator PositionWrapper(Position pos)
        {
            return new PositionWrapper { EnumValue = pos };
        }
    }

    public enum Position
    {
        Dead,
        Dying,
        Stunned,
        Sleeping,
        Resting,
        Fighting,
        Standing
    }

    public partial class SexWrapper
    {
        public Sex EnumValue
        {
            get { return (Sex)Value; }
            set { Value = (int)value; }
        }

        public static implicit operator Sex(SexWrapper wrapper)
        {
            if (wrapper == null) return default(Sex);

            return wrapper.EnumValue;
        }

        public static implicit operator SexWrapper(Sex sex)
        {
            return new SexWrapper { EnumValue = sex };
        }
    }

    public enum Sex
    {
        Neutral,
        Male,
        Female,
        Hermaphrodite
    }

    public enum Size
    {
        Minuscule,
        Tiny,
        Small,
        Medium,
        Large,
        Huge,
        Giant
    }

    public class ActionFormatter : IFormatProvider, ICustomFormatter
    {
        #region Implementation of IFormatProvider

        public ActionFormatter(Character viewer)
        {
            Viewer = viewer;
        }

        public Character Viewer { get; private set; }

        /// <summary>
        ///                     Returns an object that provides formatting services for the specified type.
        /// </summary>
        /// <returns>
        ///                     An instance of the object specified by <paramref name="formatType" />, if the <see cref="T:System.IFormatProvider" /> implementation can supply that type of object; otherwise, null.
        /// </returns>
        /// <param name="formatType">
        ///                     An object that specifies the type of format object to return. 
        ///                 </param><filterpriority>1</filterpriority>
        public object GetFormat(Type formatType)
        {
            return formatType == typeof (ICustomFormatter) ? this : null;
        }

        #endregion

        #region Implementation of ICustomFormatter

        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            var ch = arg as Character;
            if (string.IsNullOrEmpty(format) || ch == null) return arg.ToString();
            return ch.ToString(format, formatProvider);
        }

        #endregion
    }
}